#include "Avalon/IO/DelimitedReader.hpp"
#include <boost/bind.hpp>
#include "Avalon/IO/Buffer.hpp"
#include <limits.h>

using namespace Avalon;
using namespace Avalon::IO;
using namespace Avalon::Threading;
using namespace boost;
using namespace boost::signals2;
using namespace std;

DelimitedReader::DelimitedReader(Carry<Reader> source, char delimiter)
    : m_source(source),
      m_delimiter(delimiter) {}

DelimitedReader::~DelimitedReader() {}

bool DelimitedReader::IsAtEndOfDataSource() {
  return m_source->IsAtEndOfDataSource();
}

Async<int>::Ptr DelimitedReader::Read(void* destination, int size) {
  boost::lock_guard<boost::recursive_mutex> lock(m_mutex);
  assert(m_sourceRead == NULL);
  m_sourceRead = m_source->Read(&m_nextCharacter, sizeof(m_nextCharacter));
  AsyncOwner<int>::Ptr delimitedRead = AsyncOwner<int>::Create();
  m_sourceRead->ConnectFinishedSignal(bind(&DelimitedReader::OnRead, this,
    static_cast<Buffer*>(NULL), destination, size, 0, delimitedRead,
    m_sourceRead));
  return delimitedRead;
}

Async<int>::Ptr DelimitedReader::Read(Buffer* destination, int size) {
  boost::lock_guard<boost::recursive_mutex> lock(m_mutex);
  assert(m_sourceRead == NULL);
  m_sourceRead = m_source->Read(&m_nextCharacter, sizeof(m_nextCharacter));
  AsyncOwner<int>::Ptr delimitedRead = AsyncOwner<int>::Create();
  m_sourceRead->ConnectFinishedSignal(bind(&DelimitedReader::OnRead, this,
    destination, destination->GetMutableData(), size, 0, delimitedRead,
    m_sourceRead));
  return delimitedRead;
}

void DelimitedReader::OnRead(Buffer* buffer, void* destination, int sizeToRead,
    int sizeRead, const AsyncOwner<int>::Ptr& delimitedRead,
    const Async<int>::Ptr& sourceRead) {
  boost::lock_guard<boost::recursive_mutex> lock(m_mutex);
  m_sourceRead.reset();
  if(sourceRead->GetState() == BaseAsync::EXCEPTION) {
    delimitedRead->CopyException(sourceRead);
    return;
  }
  --sizeToRead;
  ++sizeRead;
  bool readDelimiter = (m_nextCharacter == m_delimiter);
  if(!readDelimiter) {
    if(buffer != NULL) {
      buffer->Reserve(sizeRead);
      destination = buffer->GetMutableData();
    }
    *(static_cast<char*>(destination) + sizeRead - 1) = m_nextCharacter;
  }
  if(readDelimiter) {
    delimitedRead->SetResult(sizeRead - 1);
    return;
  } else if(sizeToRead == 0 || m_source->IsAtEndOfDataSource()) {
    delimitedRead->SetResult(sizeRead);
    return;
  }
  m_sourceRead = m_source->Read(&m_nextCharacter, sizeof(m_nextCharacter));
  m_sourceRead->ConnectFinishedSignal(bind(&DelimitedReader::OnRead, this,
    buffer, destination, sizeToRead, sizeRead, delimitedRead, m_sourceRead));
}
